package com.gmcloud.upms.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gmcloud.common.core.exception.BaseException;
import com.gmcloud.common.mybatis.base.BasePageEntity;
import com.gmcloud.common.mybatis.plus.QueryWrapperPlus;
import com.gmcloud.common.utils.BeanUtil;
import com.gmcloud.upms.api.system.entity.SysMenu;
import com.gmcloud.upms.api.system.entity.SysRole;
import com.gmcloud.upms.api.system.entity.dto.SysMenuDto;
import com.gmcloud.upms.api.system.entity.payload.MenuQueryCriteria;
import com.gmcloud.upms.api.system.entity.vo.menu.MenuMetaVo;
import com.gmcloud.upms.api.system.entity.vo.menu.NavMenuVo;
import com.gmcloud.upms.api.system.entity.vo.menu.SysMenuVo;
import com.gmcloud.upms.biz.mapper.SysMenuMapper;
import com.gmcloud.upms.biz.mapper.SysRoleMapper;
import com.gmcloud.upms.biz.service.SysMenuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.gmcloud.common.utils.constant.CommonConstants.MENU;
import static com.gmcloud.common.utils.constant.CommonConstants.MENU_TREE_ROOT_ID;

/**
 * @author zl.sir
 * @version 1.0
 * @since 2022/8/26 16:50
 * 菜单操作实现类
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    private final SysRoleMapper sysRoleMapper;

    public SysMenuServiceImpl(SysRoleMapper sysRoleMapper) {
        this.sysRoleMapper = sysRoleMapper;
    }


    @Override
    public Set<SysMenu> setByRoleId(Long roleId) {
        return baseMapper.listMenusByRoleId(roleId);
    }

    @Override
    public List<SysMenuVo> listByRoleIds(Set<Long> ids) {
        List<SysMenu> menus = baseMapper.findListByRoleIds(ids);
        return BeanUtil.copyListProperties(menus, SysMenuVo::new);
    }

    @Override
    public List<SysMenuVo> listByUserId(Long currentUserId) {
        List<SysRole> roleVos = sysRoleMapper.findListByUserId(currentUserId);
        Set<Long> roleIds = roleVos.stream().map(SysRole::getId).collect(Collectors.toSet());
        return listByRoleIds(roleIds);
    }

    @Override
    public List<SysMenuVo> buildTree(List<SysMenuVo> menuVoList) {
        // 根节点菜单
        List<SysMenuVo> rootMenu = menuVoList.stream().
                filter(m -> Objects.equals(m.getPid(), MENU_TREE_ROOT_ID)).toList();
        List<SysMenuVo> childMenuVos = menuVoList.stream().filter(m -> Objects.nonNull(m.getPid())).toList();

        return rootMenu.stream().peek(
                sysMenuVo -> sysMenuVo.setChildren(collectTreeData(sysMenuVo, childMenuVos))
        ).toList();
    }

    @Override
    public List<SysMenuVo> collectTreeData(SysMenuVo sysMenuVo, List<SysMenuVo> menuVoList) {
        return menuVoList.stream().filter(m -> m.getPid().equals(sysMenuVo.getId()))
                .peek(m -> m.setChildren(collectTreeData(m, menuVoList))).toList();
    }

    @Override
    public List<NavMenuVo> buildMenus(List<SysMenuVo> menuVosTree) {
        List<NavMenuVo> list = new LinkedList<>();
        menuVosTree.forEach(m -> {
            NavMenuVo menuVo = new NavMenuVo();
            menuVo.setName(m.getName());
            // 一级目录需要加斜杠，不然会报警告
            menuVo.setPath(Objects.equals(MENU_TREE_ROOT_ID, m.getPid()) ? "/" + m.getPath() : m.getPath());
            MenuMetaVo menuMetaVo = new MenuMetaVo();
            menuMetaVo.setHidden(m.getHidden());
            menuMetaVo.setTitle(m.getTitle());
            menuMetaVo.setIcon(m.getIcon());
            menuMetaVo.setNoCache(m.getCache());
            menuMetaVo.setRequiresAuth(m.getRequiresAuth());
            menuMetaVo.setAlwaysShow(true);
            menuVo.setMeta(menuMetaVo);
            // 外部链接
            if (Boolean.TRUE.equals(m.getFrame())) {
                menuVo.setComponent(!StringUtils.hasLength(m.getComponent()) ? "Link" : m.getComponent());
            } else {
                if (Objects.equals(MENU_TREE_ROOT_ID, m.getPid()) && Objects.equals(m.getType(), MENU)) {  // 菜单布局
                    menuVo.setComponent(!StringUtils.hasLength(m.getComponent()) ? "Layout" : m.getComponent());
                } else if (Objects.equals(m.getType(), MENU)) {   //菜单打开新页面
                    menuVo.setComponent(!StringUtils.hasLength(m.getComponent()) ? "ParentView" : m.getComponent());
                } else if (StringUtils.hasLength(m.getComponent())) {  // 路由或按钮
                    menuVo.setComponent(m.getComponent());
                }
            }
            List<SysMenuVo> childList = m.getChildren();

            // 存在下级菜单
            if (CollUtil.isNotEmpty(childList)) {
                menuVo.setRedirect("noRedirect");
                menuVo.setChildren(buildMenus(childList));
            } else if (Objects.equals(MENU_TREE_ROOT_ID, m.getPid())) {
                NavMenuVo navMenuVo = new NavMenuVo();
                navMenuVo.setMeta(menuVo.getMeta());
                if (Boolean.FALSE.equals(m.getFrame())) {
                    navMenuVo.setPath("index");
                    navMenuVo.setName(menuVo.getName());
                    navMenuVo.setComponent(menuVo.getComponent());
                }
                menuVo.setName(null);
                menuVo.setMeta(null);
                menuVo.setChildren(Collections.singletonList(navMenuVo));
            }
            list.add(menuVo);

        });
        return list;
    }

    @Override
    public BasePageEntity<SysMenuVo> page(Page<SysMenu> page, MenuQueryCriteria queryCriteria) {
        Wrapper<SysMenu> wrapper = QueryWrapperPlus.getPredicate(queryCriteria, SysMenu::new);
        IPage<SysMenu> iPage = baseMapper.selectPage(page, wrapper);
        List<SysMenuVo> sysMenuVos = BeanUtil.copyListProperties(iPage.getRecords(), SysMenuVo::new);
        return new BasePageEntity<>(sysMenuVos, iPage.getTotal());
    }

    @Override
    public List<SysMenuVo> list(MenuQueryCriteria queryCriteria) {
        Wrapper<SysMenu> wrapper = QueryWrapperPlus.getPredicate(queryCriteria, SysMenu::new);
        List<SysMenu> sysMenus = baseMapper.selectList(wrapper);
        return BeanUtil.copyListProperties(sysMenus, SysMenuVo::new);
    }

    @Override
    public List<SysMenuVo> menuTree() {
        List<SysMenu> sysMenus = baseMapper.selectList(new QueryWrapper<SysMenu>().lambda()
                .orderByAsc(SysMenu::getMenuSort));
        List<SysMenuVo> sysMenuVos = BeanUtil.copyListProperties(sysMenus, SysMenuVo::new);
        return buildTree(sysMenuVos);
    }

    @Transactional(rollbackFor = BaseException.class)
    @Override
    public void save(SysMenuDto sysMenuDto) {
        SysMenu sysMenu = BeanUtil.copyProperties(sysMenuDto, SysMenu::new);
        baseMapper.insert(sysMenu);
        if (Objects.nonNull(sysMenu.getPid())) {
            SysMenu parentMenu = baseMapper.selectById(sysMenu.getPid());
            parentMenu.setSubCount(parentMenu.getSubCount() + 1);
            baseMapper.updateById(parentMenu);
        }
    }

    @Transactional(rollbackFor = BaseException.class)
    @Override
    public void update(SysMenuDto sysMenuDto) {
        SysMenu oldSysMenu = baseMapper.selectById(sysMenuDto.getId());
        SysMenu sysMenu = BeanUtil.copyProperties(sysMenuDto, SysMenu::new);
        baseMapper.updateById(sysMenu);
        if (!Objects.equals(oldSysMenu.getPid(), sysMenu.getPid())) {
            if (!Objects.equals(MENU_TREE_ROOT_ID, sysMenu.getPid())) {
                SysMenu parentMenu = baseMapper.selectById(sysMenu.getPid());
                parentMenu.setSubCount(parentMenu.getSubCount() + 1);
                baseMapper.updateById(parentMenu);
            }

            if (!Objects.equals(MENU_TREE_ROOT_ID, oldSysMenu.getPid())) {
                SysMenu oldParentMenu = baseMapper.selectById(oldSysMenu.getPid());
                oldParentMenu.setSubCount(oldParentMenu.getSubCount() - 1);
                baseMapper.updateById(oldParentMenu);
            }
        }
    }

    @Transactional(rollbackFor = BaseException.class)
    @Override
    public void delete(SysMenuDto sysMenuDto) {
        SysMenu sysMenu = BeanUtil.copyProperties(sysMenuDto, SysMenu::new);
        baseMapper.deleteById(sysMenu);
        if (Objects.nonNull(sysMenu.getPid())) {
            SysMenu parentMenu = baseMapper.selectById(sysMenu.getPid());
            parentMenu.setSubCount(parentMenu.getSubCount() - 1);
            baseMapper.updateById(parentMenu);
        }
    }

    @Override
    public List<SysMenuVo> findByRoleIds(List<Long> roleIds) {
        List<SysMenu> sysMenus = baseMapper.selectListByRoleIds(roleIds);
        return BeanUtil.copyListProperties(sysMenus, SysMenuVo::new);
    }
}
